今天我們來看看, 當創建一個 task 物件, 設定完要做的行為已及排程方式後, 會調用兩個方法, 他們分別代表甚麼意思。
以下方法在執行Task.Run打包新task 調用
internal static Task InternalStartNew(
            Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler,
            TaskCreationOptions options, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark)
{
    // Validate arguments.
    if (scheduler == null)
    {
        throw new ArgumentNullException("scheduler");
    }
    Contract.EndContractBlock();
    // Create and schedule the task. This throws an InvalidOperationException if already shut down.
    // Here we add the InternalTaskOptions.QueuedByRuntime to the internalOptions, so that TaskConstructorCore can skip the cancellation token registration
    Task t = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);
    t.PossiblyCaptureContext(ref stackMark);
    t.ScheduleAndStart(false);
    return t;
}
我們要來看看這兩個方法
t.PossiblyCaptureContext(ref stackMark);
t.ScheduleAndStart(false);
PossiblyCaptureContextinternal void PossiblyCaptureContext(ref StackCrawlMark stackMark)
{
    Contract.Assert(m_contingentProperties == null || m_contingentProperties.m_capturedContext == null,
        "Captured an ExecutionContext when one was already captured.");
    // In the legacy .NET 3.5 build, we don't have the optimized overload of Capture()
    // available, so we call the parameterless overload.
#if PFX_LEGACY_3_5
            CapturedContext = ExecutionContext.Capture();
#else
    CapturedContext = ExecutionContext.Capture(
        ref stackMark,
        ExecutionContext.CaptureOptions.IgnoreSyncCtx | ExecutionContext.CaptureOptions.OptimizeDefaultCase);
#endif
}
可以發現重點是調用 ExecutionContext.Capture其底層是利用 Thread 中的 method 獲取當前所在 thread 的內容並且回傳。
所以變數 CapturedContext , 經過這個方法後裡面存了 當前 thread 裡面的資料, 待用。
換句話說, task 存下了當初創建他的 thread 的執行內容。
ScheduleAndStartinternal void ScheduleAndStart(bool needsProtection)
{
    Contract.Assert(m_taskScheduler != null, "expected a task scheduler to have been selected");
    Contract.Assert((m_stateFlags & TASK_STATE_STARTED) == 0, "task has already started");
    // 設定任務開始的標記
    if (needsProtection)
    {
        if (!MarkStarted())
        {
            // A cancel has snuck in before we could get started.  Quietly exit.
            return;
        }
    }
    else
    {
        m_stateFlags |= TASK_STATE_STARTED;
    }
    if (s_asyncDebuggingEnabled)
    {
        AddToActiveTasks(this);
    }
    if (AsyncCausalityTracer.LoggingOn && (Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
    {
        //For all other task than TaskContinuations we want to log. TaskContinuations log in their constructor
        AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task: " + ((Delegate)m_action).Method.Name, 0);
    }
    try
    {
        // 把當前任務送入 Scheduler 的隊列中
        m_taskScheduler.InternalQueueTask(this);
    }
    catch (ThreadAbortException tae)
    {
        AddException(tae);
        FinishThreadAbortedTask(true, false);
    }
    catch (Exception e)
    {
        // The scheduler had a problem queueing this task.  Record the exception, leaving this task in
        // a Faulted state.
        TaskSchedulerException tse = new TaskSchedulerException(e);
        AddException(tse);
        Finish(false);
        // Now we need to mark ourselves as "handled" to avoid crashing the finalizer thread if we are called from StartNew()
        // or from the self replicating logic, because in both cases the exception is either propagated outside directly, or added
        // to an enclosing parent. However we won't do this for continuation tasks, because in that case we internally eat the exception
        // and therefore we need to make sure the user does later observe it explicitly or see it on the finalizer.
        if ((Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == 0)
        {
            // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
            Contract.Assert(
                (m_contingentProperties != null) &&
                (m_contingentProperties.m_exceptionsHolder != null) &&
                (m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
                    "Task.ScheduleAndStart(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
                    "and to have faults recorded.");
            m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
        }
        // re-throw the exception wrapped as a TaskSchedulerException.
        throw tse;
    }
}
可以發現重點在 m_taskScheduler.InternalQueueTask(this);
其把當前任務送入 Scheduler 的隊列中, 而我們之前說過, 這個 Scheduler 其實就是 TP
所以我們繼續往下看
internal void InternalQueueTask(Task task)
{
    Contract.Requires(task != null);
    task.FireTaskScheduledIfNeeded(this);
    this.QueueTask(task);
}
重點在 QueueTask
他其實是一個介面, 擔當了所有類型 Scheduler 推入任務的入口
而因為我們知道 Scheduler 是 TP , 所以直接看 TP 複寫的 QueueTask
protected internal override void QueueTask(Task task)
{
    if ((task.Options & TaskCreationOptions.LongRunning) != 0)
    {
        // Run LongRunning tasks on their own dedicated thread.
        Thread thread = new Thread(s_longRunningThreadWork);
        thread.IsBackground = true; // Keep this thread from blocking process shutdown
        thread.Start(task);
    }
    else
    {
        // Normal handling for non-LongRunning tasks.
        bool forceToGlobalQueue = ((task.Options & TaskCreationOptions.PreferFairness) != 0);
        ThreadPool.UnsafeQueueCustomWorkItem(task, forceToGlobalQueue);
    }
}
這裡可以發現兩種 case ,
UnsafeQueueCustomWorkItem 在處理連續任務區有遇到, 可以把任務放入 TP 來執行。此外在ScheduleAndStart可以看到在例外處理有
兩種 method , 他們是用來表示主要任務完成, 會觸發前面提到的連續任務區。
ScheduleAndStart 要求同步運行時實際上, 在實作場合, 創建一個 Task 但卻希望他完成後別的 Task 才繼續走的的情況非常常見。
其底層就是在 ScheduleAndStart 中調用 Scheduler 時不是使用預設的 TP 作為  Scheduler 而是使用 SynchronizationContextTaskScheduler類別作為 Scheduler , 以下查看其複寫的InternalQueueTask。
protected internal override void QueueTask(Task task)
{
    m_synchronizationContext.Post(s_postCallback, (object)task);
}
public virtual void Post(SendOrPostCallback d, Object state)
{
    ThreadPool.QueueUserWorkItem(new WaitCallback(d), state);
}
可以看見其一樣是推入 TP 來執行, 但是調用了 WaitCallback 作為參數, 該物件我往下看過, 來自外部程式碼, 推測功能是與同樣來自外部的 TP 底層 Scheduler 溝通, 使外部程式碼操作的 workerThread 用同步方式運行這個 TP 中的任務。
到此, .Net Task 算是看得差不多了, 我明天會進行這幾天文章的整理, 算是做一個懶人包給大家。
明天見 !